文件:File Uploads
建立上傳表單: 使用<form>
標籤和enctype="multipart/form-data"
屬性來建立檔案上傳表單。
驗證檔案: 在控制器中使用$request->validate()
方法來驗證上傳的檔案(如類型、大小)。
處理上傳檔案: 使用$request->file('file')->store('path')
方法將檔案儲存到指定的路徑。
Laravel 提供了簡單且強大的方式來處理文件上傳,可以輕鬆地建立文件上傳表單,處理上傳的文件,並將其儲存到檔案系統中。
step 1:建立文件上傳表單
在 resources/views
目錄下建立一個 Blade 範本檔案 upload.blade.php
,用於顯示檔案上傳表單:
<!-- resources/views/upload.blade.php -->
<!DOCTYPE html>
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>Upload File</h1>
<!-- 顯示上傳錯誤訊息 -->
@if ($errors->any())
<div>
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- 文件上傳表單 -->
<form action="{{ route('upload') }}" method="POST" enctype="multipart/form-data">
@csrf
<label for="file">Choose File:</label>
<input type="file" id="file" name="file">
<br>
<button type="submit">Upload</button>
</form>
</body>
</html>
step 2:建立文件上傳控制器
下指令 php artisan make:controller FileUploadController
生成的 app/Http/Controllers/FileUploadController.php
文件,新增處理文件上傳的邏輯:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class FileUploadController extends Controller
{
/**
* Show the file upload form.
*
* @return \Illuminate\View\View
*/
public function showForm()
{
return view('upload');
}
/**
* Handle file upload request.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
public function upload(Request $request)
{
// 驗證文件
$request->validate([
'file' => 'required|file|mimes:jpeg,png,pdf|max:2048',
]);
// 儲存檔案到本機磁碟
$filePath = $request->file('file')->store('uploads', 'public');
// 儲存文件到公共磁碟
// $filePath = $request->file('file')->store('uploads');
return redirect()->back()->with('success', 'File uploaded successfully!')->with('file', $filePath);
}
}
也可以用前一天提到的創建表單驗證,在用依賴注入的方法引入控制器
// Http/Requests/FileUploadRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class FileUploadRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'file' => ['required', 'file', 'mimes:jpeg,png,pdf', 'max:2048'],
];
}
/**
* Get custom messages for validator errors.
* 取得已定義驗證規則的錯誤訊息: 自定義錯誤訊息
*
* @return array
*/
public function messages(): array
{
return [
'file.required' => '檔案是必填的',
'file.file' => '必須是文件',
'file.mimes' => '文件類型必須是 jpeg, png, pdf',
'file.max' => '檔案最大只能 2048'
];
}
}
<?php
namespace App\Http\Controllers;
use App\Http\Requests\FileUploadRequest;
use Illuminate\Http\RedirectResponse;
use Illuminate\View\View;
class FileUploadController extends Controller
{
/**
* Show the file upload form.
*
* @return View
*/
public function showForm(): View
{
return view('upload');
}
/**
* Handle file upload request.
*
* @param FileUploadRequest $request
* @return RedirectResponse
*/
public function upload(FileUploadRequest $request): RedirectResponse
{
// 驗證文件
$validatedData = $request->validated();
// 儲存檔案到本機磁碟
// $filePath = $validatedData['file']->store('uploads', 'public');
// 或者,存儲文件到公共磁碟
$filePath = $validatedData['file']->store('uploads');
return redirect()->back()->with('success', '檔案上傳成功!')->with('file', $filePath);
}
}
🐘 補充說明:
從上圖可知道存放位置會隨指定不同,其中 'public'(存本機) 和 'uploads'(存公共磁碟)差異來自於公用磁碟可以讓使用隨意打就取得路徑
只要下指令php artisan storage:link
後,可以看到提示The [public/storage] link has been connected to [storage/app/public].
step 3:設定路由
在 routes/web.php
文件中設定路由:
use App\Http\Controllers\FileUploadController;
Route::get('/upload', [FileUploadController::class, 'showForm'])->name('upload.form');
Route::post('/upload', [FileUploadController::class, 'upload'])->name('upload');
文件:Amazon S3 Compatible Filesystems
本地儲存: 預設情況下,Laravel 使用本地存儲,檔案保存在storage/app
目錄下。
公共儲存: 使用public
磁碟將檔案儲存在storage/app/public
目錄下,並且可以透過 URL 存取。
雲端儲存: 設定config/filesystems.php
檔案以使用雲端儲存服務(如 Amazon S3)。
Laravel 的檔案儲存系統支援多種儲存方式,包括本機儲存和雲端儲存(如 Amazon S3)。
本地儲存
在 upload
方法中,使用 store
方法將檔案儲存到 storage/app/public/uploads
目錄。
可以使用 public
磁碟存取這些檔案。
雲端儲存
要使用雲端儲存(如 Amazon S3),首先需要安裝相關的套件並在 .env
檔案中配置相應的儲存設定。以下是一個簡單的 S3 配置範例:
FILESYSTEM_DRIVER=s3
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_DEFAULT_REGION=your-region
AWS_BUCKET=your-bucket
在 config/filesystems.php
中設定 s3
驅動程式:
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
],
然後在控制器中可以使用 store
方法將檔案儲存到 S3:
$filePath = $request->file('file')->store('uploads', 's3');
儲存檔案: 使用
store
方法將檔案儲存到指定路徑。
刪除檔案: 使用Storage::delete()
方法刪除檔案。
下載檔案: 使用Storage::download()
方法提供檔案下載。
Laravel 提供了一種簡單的方法來存取和操作文字項。
儲存檔案
使用 store
方法來儲存檔案:
$filePath = $request->file('file')->store('uploads');
刪除文件
可以使用 Storage
門面來刪除檔案:
use Illuminate\Support\Facades\Storage;
Storage::delete($filePath);
下載文件
要讓使用者能夠下載文件,可以使用 Storage
門面提供的下載方法:
return Storage::download($filePath);
運用文件上傳和儲存,練習把使用者頭像上傳和管理頭像。
step 1 - 規劃
上傳需要畫面也就是要先出第一支 api 是畫面渲染用的 GET;還需要有上傳檔案的媒介也就是第二支 api POST,這可能要把資料寫入資料庫到時候要拿內容渲染在畫面上以外,還要可以抓到資料刪除。
管理的部分,刪除和下載個別也需要一支 api 做資料庫處理並渲染在畫面上。
參考文章:
Laravel 上傳/刪除圖片 | Upload and Delete Image
laravel文件存储、删除、移动等操作
step 2 - 設定資料庫
下指令 php artisan make:model Avatar -m
建立模型和遷移文件,並且在遷移文件設定表格欄位名稱和型別。
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('avatars', function (Blueprint $table) {
$table->id();
$table->string('file_path');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('avatars');
}
};
step 3 - 建立資料庫
指令 php artisan migrate
建立資料庫
step 4 - 建立表單驗證
指令 php artisan make:request AvatarRequest
,在表單驗證中建立規則和錯誤訊息回傳。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AvatarRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules(): array
{
return [
'avatar' => ['required', 'file', 'mimes:jpg,jpeg,png', 'max:2048'],
];
}
/**
* Summary of messages
* @return array
*/
public function messages(): array
{
return [
'file.required' => '檔案是必填的',
'file.file' => '必須是文件',
'file.mimes' => '文件類型必須是 jpeg, png, pdf',
'file.max' => '檔案最大只能 2048'
];
}
}
step 5 - 建立控制器
指令 php artisan make:controller AvatarController
,建立一個控制器來處理頭像的上傳、儲存、刪除和下載,其中表單驗證的部分改用創建的驗證類引入。
<?php
namespace App\Http\Controllers;
use App\Http\Requests\AvatarRequest;
use App\Models\Avatar;
use Illuminate\Support\Facades\Storage;
use \Illuminate\Http\RedirectResponse;
use \Illuminate\Contracts\View\View;
class AvatarController extends Controller
{
/**
* Summary of index
* @return View
*/
public function index(): View
{
$avatars = Avatar::all();
return view('upload-avatar', compact('avatars'));
}
/**
* Summary of upload
* @param AvatarRequest $request
* @return RedirectResponse
*/
public function upload(AvatarRequest $request): RedirectResponse
{
// 取得驗證過的數據
$validated = $request->validated();
// 取得上傳的文件
$file = $validated['avatar'];
$filename = $file->hashName();
$path = $file->storeAs('public/avatars', $filename);
// 儲存文件資訊到資料庫
$filePath = Avatar::query()->create(['file_path' => $filename]);
return redirect()->back()->with('success', 'Avatar uploaded successfully!');
}
/**
* Summary of delete
* @param mixed $filename
* @return RedirectResponse
*/
public function delete($filename): RedirectResponse
{
$avatar = Avatar::where('file_path', $filename)->first();
if ($avatar) {
$path = "public/avatars/{$filename}";
if (Storage::exists($path)) {
Storage::delete($path);
$avatar->delete();
return redirect()->back()->with('success', 'Avatar deleted successfully!');
}
}
return redirect()->back()->with('error', 'File not found.');
}
/**
* Summary of download
*
* @param mixed $filename
* @return mixed|\Illuminate\Http\RedirectResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse
*/
public function download($filename): mixed
{
if (Storage::disk('public')->exists("avatars/{$filename}")) {
return response()->download(storage_path("app/public/avatars/{$filename}"));
}
return redirect()->back()->with('error', 'File not found.');
}
}
step 6 - 建立視圖
參考文章:laravel compact的用法
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Avatar</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<!-- 上傳成功訊息 -->
@if (session('success'))
<p class="text-primary">{{ session('success') }}</p>
@endif
<!-- 上傳錯誤訊息 -->
@if ($errors->any())
<div>
<ul>
@foreach ($errors->all() as $error)
<li class="text-warning">{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- 上傳表單 -->
<form action="{{ route('upload.avatar') }}" method="POST" enctype="multipart/form-data">
@csrf
<label for="avatar" class="form-label">Choose Avatar:</label>
<input type="file" id="avatar" name="avatar" class="form-control">
<br>
<button type="submit" class="btn btn-primary rounded-pill my-3" style="width: 15%">上傳</button>
</form>
<!-- 頭像列表 -->
<ul class="row ps-0">
@foreach ($avatars as $avatar)
<li class="col-3 d-flex flex-column justify-content-between" style="list-style:none">
<img src="{{ Storage::url("avatars/{$avatar->file_path}") }}" alt="{{ $avatar->file_path }}" class="rounded w-100 d-block">
<form action="{{ route('delete.avatar', ['file_path' => $avatar->file_path]) }}" method="POST" style="display:inline;">
@csrf
@method('DELETE')
<div class="d-flex flex-column">
<button type="submit" class="btn btn-outline-danger rounded-pill my-1">刪除</button>
<a href="{{ route('download.avatar', ['file_path' => $avatar->file_path]) }}" class="btn btn-outline-primary rounded-pill">下載</a>
</div>
</form>
</li>
@endforeach
</ul>
</div>
</body>
</html>
step 7 - 建立路由
在 web.php
建立路由
<?php
use App\Http\Controllers\AvatarController;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('avatars', [AvatarController::class, 'index'])->name('avatars.index');
Route::post('upload-avatar', [AvatarController::class, 'upload'])->name('upload.avatar');
Route::delete('delete-avatar/{file_path}', [AvatarController::class, 'delete'])->name('delete.avatar');
Route::get('download-avatar/{file_path}', [AvatarController::class, 'download'])->name('download.avatar');
step 8 - 建立一個符號連結讓瀏覽器收資料
指令 php artisan storage:link
用於建立符號鏈接,將 public/storage
目錄連結到 storage/app/public
目錄。
🔔 提醒:
只需要下一次即可,也就是練習的時候下過指令,這一步就可以跳過。
step 9 - 測試